%% Author: maciek
%% Created: 15-04-2011
%% Description: TODO: Add description to peer_fsm
-module(call_fsm).
-behaviour(gen_fsm).
-export([start_link/2,
		 init/1,
		 initial/2,
		 got_authreq/1,
		 new_sent/1,
		 waiting_for_authreq/2,
		 get_isec_number/1,
		 get_osec_number/1,
		 got_accept/1,
		 got_fullframe/6,
		got_ack/6,
		 sent_fullframe/1,
		 handle_event/3,
		 handle_sync_event/4,
		 waiting_for_accept/2,
		 got_answer/1,
		 accepted/2,
		sent_ack/1,
		terminate/3,
		got_ougoing_media/2,
		answered/2,
		got_hangup/1
		]).
-record(state, {callfsmname,
				retry_count=0,
				oseqno=0,
				iseqno=0,
				callcontrollerservername,ffmediaframescount=0,hangedup=false}).
-define(ACK_TIMEOUT,1000).

start_link(CallFsmName,CallControllerServerName) ->
	io:format("Starting FSM [~p] for CallController ~p~n",[CallFsmName,CallControllerServerName]),
    gen_fsm:start_link({local, CallFsmName}, ?MODULE, [CallFsmName,CallControllerServerName], []).


get_osec_number(CallFSMName) ->
	gen_fsm:sync_send_all_state_event(CallFSMName, get_osec_number).
get_isec_number(CallFSMName) ->
	gen_fsm:sync_send_all_state_event(CallFSMName, get_isec_number).

sent_fullframe(CallFSMName) ->
	gen_fsm:send_all_state_event(CallFSMName,sent_fullframe).
sent_ack(CallFSMName) ->
	gen_fsm:send_all_state_event(CallFSMName,sent_ack).
got_fullframe(CallFSMName,Frametype,FrameSubclass,ISeqNo,OSeqNo,TS) ->
	gen_fsm:send_all_state_event(CallFSMName,{got_fullframe,Frametype,FrameSubclass,ISeqNo,OSeqNo,TS}).

got_ack(CallFSMName,Frametype,FrameSubclass,ISeqNo,OSeqNo,TS) ->
	gen_fsm:send_all_state_event(CallFSMName,{got_ack,Frametype,FrameSubclass,ISeqNo,OSeqNo,TS}).

got_ougoing_media(CallFSMName,Media) ->
	 gen_fsm:send_event(CallFSMName,{got_outgoing_media,Media}).
init([CallFsmName,CallControllerServerName])->
	S=#state{callcontrollerservername=CallControllerServerName,callfsmname=CallFsmName},
    {ok, initial, S}.

got_hangup(CallFSMName) ->
	gen_fsm:send_event(CallFSMName,got_hangup ).	


got_authreq(FSMName) ->
    gen_fsm:send_event(FSMName,got_authreq ).

got_accept(FSMName) ->
    gen_fsm:send_event(FSMName,got_accept ).
got_answer(FSMName) ->
	 gen_fsm:send_event(FSMName,got_answer ).
new_sent(FSMName) ->
  gen_fsm:send_event(FSMName,new_sent ).

initial(new_sent,SD) ->
	%%TODO oseqno? iseqno?
	io:format("[FMS]travesing to waiting_for_authreq~n"),
	{next_state,waiting_for_authreq,SD,?ACK_TIMEOUT}.

waiting_for_authreq(got_authreq,SD) ->
	io:format("[FMS]Got authreq for callcontroller ~p~n",[SD#state.callcontrollerservername]),
	call_controller:send_authrep(SD#state.callcontrollerservername),
	{next_state, waiting_for_accept, SD,?ACK_TIMEOUT}.


waiting_for_accept(got_authreq,SD) ->
		io:format("[FMS]Got authreq for callcontroller ~p~n",[SD#state.callcontrollerservername]),
	call_controller:send_authrep(SD#state.callcontrollerservername),
	{next_state, waiting_for_accept, SD,?ACK_TIMEOUT};


waiting_for_accept(got_accept,SD) ->
	io:format("[FMS]Got accept for callcontroller ~p~n",[SD#state.callcontrollerservername]),
	
	{next_state, accepted, SD}.
accepted(got_answer,SD) ->
	io:format("[FMS]Got ANSWER for callcontroller ~p~n",[SD#state.callcontrollerservername]),
	{next_state, answered, SD}.

answered({got_outgoing_media,Media},SD) ->
	io:format("===>[MEDIA][FMS]Got some media in answered state- relaying to peer sendinf FULLFRAME~n"),
	NewStateData=SD#state{ffmediaframescount=SD#state.ffmediaframescount+1},
	call_controller:send_media_fullframe(SD#state.callcontrollerservername,Media),
	
	{next_state,answered,NewStateData};
answered({got_outgoing_media,Media},SD) ->
	io:format("===>[MEDIA][FMS]Got some media in answered state- relaying to peer sending MINIFRAME~n"),
	{next_state,answered,SD};

answered(got_hangup,StateData) ->
	io:format("===>Got hangup terminating gracefully ==================~n"),
	NewStateData=StateData#state{hangedup=true},
	{next_state,answered,NewStateData}.

handle_event({got_fullframe,Frametype,FrameSubclass,ISeqNo,OSeqNo,TS}, StateName, StateData) when FrameSubclass=:=authreq; FrameSubclass=:=ping; FrameSubclass=:=lagrq; FrameSubclass=:=ack  ->
	io:format("[FMS]Incrementing ISEQNO~n"),
	io:format("[FMS][NOT ACKing] FullFrame, Received Frametype ~p Subclass ~p~n",[Frametype,FrameSubclass]),
	
	NewStateData=StateData#state{iseqno=StateData#state.iseqno+1},
	{next_state,StateName,NewStateData};

handle_event({got_fullframe,Frametype,FrameSubclass,ISeqNo,OSeqNo,TS}, StateName, StateData) ->
	io:format("[FSM]Incrementing ISEQNO~n"),
	io:format("[FSM]Sending ACK FOR FullFrame, Received Frametype ~p Subclass ~p  ~n",[Frametype,FrameSubclass]),
	call_controller:send_ack(StateData#state.callcontrollerservername,ISeqNo,OSeqNo,TS),
	NewStateData=StateData#state{iseqno=StateData#state.iseqno+1},
	{next_state,StateName,NewStateData};

handle_event({got_ack,Frametype,FrameSubclass,ISeqNo,OSeqNo,TS}, StateName, StateData) ->
	io:format("[FMS]GOT ACK~n"),
	{next_state,StateName,StateData};



handle_event({got_outgoing_media,Media},StateName, StateData) ->
	io:format("[FMS]Media NOT IN ANSWERED STATE"),
	{next_state,StateName,StateData};

handle_event(sent_fullframe, StateName, StateData) ->
	io:format("[FMS]Incrementing OSEQNO~n"),
	NewStateData=StateData#state{oseqno=StateData#state.oseqno+1},
	{next_state,StateName,NewStateData};
handle_event(sent_ack, _StateName, StateData=#state{hangedup=true}) ->
	io:format("ACK Sent, terminating call controller, terminating fsm~n"),
	call_controller:stop(StateData#state.callcontrollerservername),
	{stop,normal,StateData};
handle_event(sent_ack, StateName, StateData) ->
	{next_state,StateName,StateData};

handle_event(Event, StateName, StateData) ->
	io:format("[FMS]=============UNRECOGNIZED EVENT ~p IN STATE ~p~n",[Event,StateName]),
	{next_state,StateName,StateData}.

handle_sync_event(get_osec_number, _From, StateName, StateData) ->
	{reply,StateData#state.oseqno,StateName,StateData};
handle_sync_event(get_isec_number, _From, StateName, StateData) ->
	{reply,StateData#state.iseqno,StateName,StateData}.

terminate(Reason, StateName, StateData) ->
    ok.
